﻿///////////////////////////////////////////////////////////////////////////////
//
// This file is part of the BassDrop project
//   https://code.google.com/p/bass-drop/
//
//
// Creator:	Gergely Krajcsovszki (krajcsovszkig@hotmail.com)
// Date:	2013-09-21 19:35
//
//
// Copyright © 2013-2014. Gergely Krajcsovszki
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BassDrop.Core.Common.Cloud;
using BassDrop.Core.Common.Media;

namespace BassDrop.Core.Common.Plugins
{
	/// <summary>
	/// Stores searchable references to loaded plugins
	/// </summary>
	public abstract class PluginManagerBase : IDisposable
	{
		/// <summary>
		/// Event fired when the list of plugins change
		/// </summary>
		public event EventHandler PluginsChanged;

		/// <summary>
		/// Fire a new PluginsChanged event
		/// </summary>
		/// <param name="e"></param>
		private void OnPluginsChanged( EventArgs e )
		{
			if ( PluginsChanged != null )
			{
				PluginsChanged( this, e );
			}
		}

		/// <summary>
		/// Plugin instances in a map
		/// 
		/// Keys:
		///		supported protocols for the plugin
		///		
		/// Values:
		///		plugin descriptor objects
		///				
		///	A plugin may appear for multiple keys (if it supports multiple protocols)
		/// </summary>
		private Dictionary<string, BassDropPluginBase> m_pluginInstances = new Dictionary<string, BassDropPluginBase>(); // TODO: átfedések kezelése

		/// <summary>
		/// Register a new plugin. A plugin can be registered multiple times, once for each protocol, or once with all the protocols supplied.
		/// </summary>
		/// <param name="plugin">a plugin descriptor object</param>
		/// <param name="protocols">the list of protocols supported by the plugin</param>
		public void AddPlugin( BassDropPluginBase plugin, params string[] protocols )
		{
			if ( !m_pluginInstances.ContainsValue( plugin ) )
			{
				plugin.InstancesChanged -= plugin_InstancesChanged;
				plugin.InstancesChanged += plugin_InstancesChanged;
				plugin.SettingsChanged -= plugin_SettingsChanged;
				plugin.SettingsChanged += plugin_SettingsChanged;
				plugin.PropertyChanged -= plugin_PropertyChanged;
				plugin.PropertyChanged += plugin_PropertyChanged;
				OnPluginsChanged( EventArgs.Empty );
			}

			foreach ( var item in protocols )
			{
				m_pluginInstances[item] = plugin;
			}
		}

		/// <summary>
		/// Enumerates the descriptor objects of the currently loaded plugins
		/// </summary>
		/// <returns></returns>
		public IEnumerable<BassDropPluginBase> GetPlugins()
		{
			return m_pluginInstances.Values.AsEnumerable().Distinct();
		}

		/// <summary>
		/// Returns the appropriate MediaPlayer instance for the passed extension
		/// </summary>
		/// <param name="extension">A media file extension with a leading dot</param>
		/// <returns>MediaPlayer instance for the passed extension or null if none found</returns>
		public async Task<MediaDecoderBase> GetMediaPlayerForFileType( string extension )
		{
			if ( m_pluginInstances.ContainsKey( extension ) )
			{
				return await m_pluginInstances[extension].GetStaticInstance() as MediaDecoderBase;
			}

			return null;
		}

		/// <summary>
		/// Returns the default cloud connection for the application, this will be used to store the settings
		/// </summary>
		/// <returns></returns>
		public CloudConnectionBase GetDefaultCloudProvider()
		{
			foreach ( var plugin in GetCloudProviders() )
			{
				foreach ( var instance in plugin.GetInstances() )
				{
					var cloudProvider = instance as CloudConnectionBase;

					if ( cloudProvider != null && cloudProvider.IsDefault )
					{
						return cloudProvider;
					}
				}
			}

			return null;
		}

		/// <summary>
		/// Returns the list of available cloud provider plugins
		/// </summary>
		/// <returns></returns>
		public IEnumerable<BassDropPluginBase> GetCloudProviders()
		{
			return from plugin in m_pluginInstances.Values
				   where plugin.PluginType == PluginType.CloudProvider
				   select plugin;
		}

		/// <summary>
		/// Returns if a file with the passed extension can be played
		/// </summary>
		/// <param name="extension">A file extension with a leading dot</param>
		/// <returns></returns>
		public bool CanPlay( string extension )
		{
			return m_pluginInstances.ContainsKey( extension );
		}

		/// <summary>
		/// Save the settings of the loaded plugins
		/// </summary>
		private void SavePluginSettings()
		{
			foreach ( var item in m_pluginInstances.Values )
			{
				item.Save( Controller.Instance.ApplicationSettings );
			}
		}

		#region IDisposable Members

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
		/// </summary>
		public void Dispose()
		{
			foreach ( var item in m_pluginInstances.Values )
			{
				item.Dispose();
			}
		}

		#endregion

		private void plugin_SettingsChanged( object sender, System.ComponentModel.PropertyChangedEventArgs e )
		{
			SavePluginSettings();
		}

		private void plugin_PropertyChanged( object sender, System.ComponentModel.PropertyChangedEventArgs e )
		{
			OnPluginsChanged( EventArgs.Empty );
		}

		private void plugin_InstancesChanged( object sender, EventArgs e )
		{
			OnPluginsChanged( EventArgs.Empty );
			SavePluginSettings();
		}
	}
}
